package tables

import (
	"fmt"
	"peilian-api/app/global/errors"
	"peilian-api/app/global/variable"
	"peilian-api/app/model"
	"time"

	"gorm.io/gorm"
)

const (
	SchoolType     string = "school"
	AcademyType    string = "academy"
	ProfessionType string = "profession"
	GradeType      string = "grade"
)

//客户构成表，主要指的是学校-学院-专业-年级
//自关联关系，分级存储

type Constitute struct {
	Model
	Type       string       `gorm:"type:varchar(15);not null;comment:类型,school/academy/profession/grade"`
	Name       string       `gorm:"type:varchar(255);not null;comment:构成要素名称"`
	BelongToID *uint        `gorm:"comment:父级构成要素ID"`
	BelongTo   *Constitute  `gorm:"foreignKey:BelongToID"`
	Member     []Constitute `gorm:"foreignKey:BelongToID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
	AgentID    *uint        `gorm:"type:uint;comment:客户信息表外键"`
	Agent      *Agent       `gorm:"foreignKey:AgentID"`
	Users      []User
}

func (Constitute) TableName() string {
	return "constitute"
}

func (c *Constitute) GetAllUsers(grade string) ([]User, error) {
	if grade == "0" {
		grade = ""
	}
	users := make([]User, 0)
	filter := struct {
		Grade string
	}{
		Grade: grade,
	}
	if err := variable.DB.MySQL.Preload("Users", &filter).Preload("Member").First(c, c.ID).Error; err != nil {
		return nil, err
	}
	users = append(users, c.Users...)
	for _, v := range c.Member {
		nestUsers, err := v.GetAllUsers(grade)
		if err == nil {
			users = append(users, nestUsers...)
		}
	}

	return users, nil
}

// 查询信息
func (c *Constitute) InsertInfo(cnt Constitute) (Constitute, error) {
	var insertInfo map[string]interface{}
	insertInfo = make(map[string]interface{}, 0)
	insertInfo["created_at"] = time.Now()
	insertInfo["updated_at"] = time.Now()
	insertInfo["type"] = cnt.Type
	insertInfo["name"] = cnt.Name
	if cnt.BelongToID != nil {
		insertInfo["belong_to_id"] = *cnt.BelongToID
	}
	if cnt.AgentID != nil {
		insertInfo["agent_id"] = *cnt.AgentID
	}
	if d := variable.DB.MySQL.Model(&Constitute{}).Create(insertInfo); d.Error != nil {
		return cnt, d.Error
	}
	return cnt, nil
}

// 插入数据
func (c *Constitute) InsertInfoMap(insertInfo map[string]interface{}) error {
	insertInfo["created_at"] = time.Now()
	insertInfo["updated_at"] = time.Now()
	if insertInfo["belong_to_id"] == nil {
		insertInfo["belong_to_id"] = 0
	}
	if insertInfo["agent_id"] == nil {
		insertInfo["agent_id"] = 0
	}
	d := variable.DB.MySQL.Model(&Constitute{}).Create(insertInfo)
	if d.Error != nil {
		return d.Error
	}
	return nil
}

// 根据条件更新数据数据
func (c *Constitute) UpdateInfoByWhereStr(updateInfo map[string]interface{}, whereStr string) error {
	if d := variable.DB.MySQL.Model(&Constitute{}).Where(whereStr).Updates(updateInfo); d.Error != nil {
		return d.Error
	}
	return nil
}

// 根据条件删除数据
func (c *Constitute) DeleteInfoByWhere(whereStr string) error {
	var updateMap map[string]interface{}
	updateMap = make(map[string]interface{}, 0)
	updateMap["deleted_at"] = gorm.DeletedAt{time.Now(), true}
	if d := variable.DB.MySQL.Model(&Constitute{}).Where(whereStr).Updates(updateMap); d.Error != nil {
		return d.Error
	}
	return nil
}

// 反向查询
func GetInfoById(id int) (cons Constitute, err error) {
	d := variable.DB.MySQL.Where("id = ?", id).Preload("BelongTo").Preload("BelongTo").Preload("BelongTo").Find(&cons)
	return cons, d.Error
}

// 关联级别 根据最低级别数据 查二级内容
func GetSecondLevelById(id int) (cons *Constitute, err error) {
	d := variable.DB.MySQL.Where("id = ?", id).Preload("BelongTo").Find(&cons)
	return cons, d.Error
}

// 关联级别 根据最低级别数据 查三级内容
func GetThirdLevelById(id int) (cons *Constitute, err error) {
	d := variable.DB.MySQL.Where("id = ?", id).Preload("BelongTo").Preload("BelongTo").Find(&cons)
	return cons, d.Error
}

// 从父到子查询
func GetChildSecondLevelById(id int) (cons *Constitute, err error) {
	d := variable.DB.MySQL.Where("id = ?", id).Preload("Member").Find(&cons)
	return cons, d.Error
}

// 根据belong_to_id查询数据
func (c *Constitute) GetConstituteByBelongToId(belongToId int) ([]model.CfgInfo, error) {
	var cfi []model.CfgInfo
	d := variable.DB.MySQL.Model(&c).Where("belong_to_id = ?", belongToId).Find(&cfi)
	if d.RowsAffected == 0 {
		return cfi, fmt.Errorf("数据为空")
	}
	return cfi, nil
}

// 根据type查询数据
func (c *Constitute) GetConstituteByType(typ string) ([]model.CfgInfo, error) {
	var cfi []model.CfgInfo
	d := variable.DB.MySQL.Model(&c).Where("type = ?", typ).Find(&cfi)
	if d.RowsAffected == 0 {
		return cfi, fmt.Errorf("数据为空")
	}
	return cfi, nil
}

// 根据id查询数据
func (c *Constitute) GetConstituteById(id int) (model.CfgInfo, error) {
	var cfi model.CfgInfo
	d := variable.DB.MySQL.Model(&c).Where("id = ?", id).Find(&cfi)
	if d.RowsAffected == 0 {
		return cfi, fmt.Errorf("数据为空")
	}
	return cfi, nil
}

// 根据where查询信息
func (c *Constitute) GetInfoByWhere(where map[string]interface{}) (Constitute, error) {
	var ct Constitute
	if d := variable.DB.MySQL.Model(&Constitute{}).Where(where).Find(&ct); d.Error != nil {
		return ct, d.Error
	}
	return ct, nil
}

// 根据学校信息查询  学校-学院-专业-年级
func GetInfoByType(typ int, name string) ([]Constitute, error) {
	where := ""
	if typ == 1 {
		where = "name = '" + name + "'"
	} else if typ == 2 {
		where = "id = '" + name + "'"
	}
	var ts []Constitute

	if d := variable.DB.MySQL.Model(&Constitute{}).Where(where).Preload("Member").FindInBatches(&ts, 1000, func(tx *gorm.DB, batch int) error {
		for _, result := range ts {
			for i := 0; i < len(result.Member); i++ {
				if d := variable.DB.MySQL.Model(&Constitute{}).Where("belong_to_id in (?)", result.Member[i].ID).Preload("Member").Find(&result.Member[i].Member); d.Error != nil {
					return d.Error
				}
			}
		}
		return nil
	}); d.Error != nil {
		return ts, fmt.Errorf("数据查询异常")
	}
	return ts, nil
}

// 根据学院id 查询该学校下的所有专业
func (c *Constitute) GetInfoByAcademy(academyId int) ([]model.CfgInfo, error) {
	var ts []model.CfgInfo
	var cs Constitute
	if d := variable.DB.MySQL.Model(&Constitute{}).Where("id = ?", academyId).Preload("BelongTo").FindInBatches(&cs, 2, func(tx *gorm.DB, batch int) error {
		if len(cs.Member) == 0 {
			return nil
		}
		if d := variable.DB.MySQL.Model(&Constitute{}).Where("belong_to_id = ?", *cs.BelongToID).Preload("Member").FindInBatches(&cs.Member, 100, func(tx *gorm.DB, batch int) error {
			for _, result := range cs.Member {
				if d := variable.DB.MySQL.Model(&Constitute{}).Where("belong_to_id in (?)", result.ID).Preload("Member").Find(&ts); d.Error != nil {
					return d.Error
				}
			}
			return nil
		}); d.Error != nil {
			return d.Error
		}
		return nil
	}); d.Error != nil {
		return ts, fmt.Errorf(errors.DatabaseMySqlOptionFail)
	}
	return ts, nil
}

// 根据学校id 查询该学校下的所有的专业
func (c *Constitute) GetInfoByShoolId(schoolId int) ([]model.CfgInfo, error) {
	var ts []model.CfgInfo
	var cs []Constitute
	if d := variable.DB.MySQL.Model(&Constitute{}).Where("belong_to_id = ?", schoolId).FindInBatches(&cs, 100, func(tx *gorm.DB, batch int) error {
		for _, result := range cs {
			var is []model.CfgInfo
			if d := variable.DB.MySQL.Model(&Constitute{}).Where("belong_to_id in (?)", result.ID).Find(&is); d.Error != nil {
				return d.Error
			}
			ts = append(ts, is...)
		}
		return nil
	}); d.Error != nil {
		return ts, fmt.Errorf(errors.DatabaseMySqlOptionFail)
	}
	return ts, nil
}

// 根据学校id 查询该学校下的所有学院以及专业
func (c *Constitute) GetAllInfoBySchoolId(schoolId int) ([]model.CfgInfos, error) {
	var out, ts []model.CfgInfos
	var cs []Constitute

	if d := variable.DB.MySQL.Model(&Constitute{}).Where("belong_to_id = ?", schoolId).FindInBatches(&cs, 1000, func(tx *gorm.DB, batch int) error {
		for i := 0; i < len(cs); i++ {
			ts = append(ts, model.CfgInfos{Id: cs[i].ID, Name: cs[i].Name, Type: cs[i].Type})
			if d := variable.DB.MySQL.Model(&Constitute{}).Where("belong_to_id in (?)", ts[i].Id).Find(&cs[i].Member); d.Error != nil {
				ts[i].Value = []model.CfgInfos{}
			}
			if len(cs[i].Member) == 0 {
				ts[i].Value = []model.CfgInfos{}
				continue
			}
			for j := 0; j < len(cs[i].Member); j++ {
				ts[i].Value = append(ts[i].Value, model.CfgInfos{Id: cs[i].Member[j].ID, Name: cs[i].Member[j].Name, Type: cs[i].Member[j].Type, Value: []model.CfgInfos{}})
			}
		}
		return nil
	}); d.Error != nil {
		return ts, fmt.Errorf(errors.DatabaseMySqlOptionFail)
	}

	// 根据id查询数据
	var cons Constitute
	if d := variable.DB.MySQL.Model(&Constitute{}).Where("id = ?", schoolId).Find(&cons); d.Error != nil {
		return out, nil
	}
	out = append(out, model.CfgInfos{Id: cons.ID, Name: cons.Name, Type: cons.Type, Value: []model.CfgInfos{}})
	// 组装数据
	if len(ts) != 0 {
		out[0].Value = ts
	}
	return out, nil
}

// tree 客户名称-学校-学院-专业
func (c *Constitute) GetAllInfoTree(schoolId uint) ([]model.CfgInfos, error) {
	var out []model.CfgInfos
	var cs []Constitute
	var whereStr = "type = 'school'"
	// 获取agent_id
	if schoolId != 0 {
		whereStr = fmt.Sprintf("id = %d", schoolId)
	}
	if d := variable.DB.MySQL.Model(&Constitute{}).Where(whereStr).FindInBatches(&cs, 1000, func(tx *gorm.DB, batch int) error {
		for i := 0; i < len(cs); i++ {
			var ts []model.CfgInfos
			ts = append(ts, model.CfgInfos{Id: cs[i].ID, Name: cs[i].Name, Type: cs[i].Type, Value: []model.CfgInfos{}})
			if d := variable.DB.MySQL.Model(&Constitute{}).Where("belong_to_id in (?)", cs[i].ID).Find(&cs[i].Member); d.Error != nil {
				ts[0].Value = []model.CfgInfos{}
				continue
			}
			for j := 0; j < len(cs[i].Member); j++ {
				ts[0].Value = append(ts[0].Value, model.CfgInfos{Id: cs[i].Member[j].ID, Name: cs[i].Member[j].Name, Type: cs[i].Member[j].Type, Value: []model.CfgInfos{}})
				if d := variable.DB.MySQL.Model(&Constitute{}).Where("belong_to_id in (?)", cs[i].Member[j].ID).Find(&cs[i].Member[j].Member); d.Error != nil {
					ts[0].Value[j].Value = []model.CfgInfos{}
					continue
				}
				if len(cs[i].Member[j].Member) != 0 {
					for k := 0; k < len(cs[i].Member[j].Member); k++ {
						ts[0].Value[j].Value = append(ts[0].Value[j].Value, model.CfgInfos{Id: cs[i].Member[j].Member[k].ID, Name: cs[i].Member[j].Member[k].Name, Type: cs[i].Member[j].Member[k].Type, Value: []model.CfgInfos{}})
					}
				} else {
					ts[0].Value[j].Value = []model.CfgInfos{}
				}
			}

			var at = Agent{}
			if d := variable.DB.MySQL.Model(&Agent{}).Where("id = ?", cs[i].AgentID).Find(&at); d.Error != nil {
				return d.Error
			}
			if at.ID == 0 {
				continue
			}
			out = append(out, model.CfgInfos{
				Id:    at.ID,
				Name:  at.AgentName,
				Type:  "agent",
				Value: ts,
			})
		}
		return nil
	}); d.Error != nil {
		return out, fmt.Errorf(errors.DatabaseMySqlOptionFail)
	}
	return out, nil
}

// 根据学院id 查询该学院下的所有专业
func (c *Constitute) GetProfessionByAcademyId(academyId int) ([]model.CfgInfo, error) {
	var ts []model.CfgInfo
	if d := variable.DB.MySQL.Model(&Constitute{}).Where("belong_to_id in (?)", academyId).Find(&ts); d.Error != nil {
		return ts, d.Error
	}
	return ts, nil
}

// 将查询的数据组装成特定的json格式 {"清华大学":{"金融学院":{"统计学":true}}}
func AssembleGivenData(cons []Constitute) map[string]map[string]map[string]uint {
	mp := map[string]map[string]map[string]uint{}
	for _, school := range cons {
		a := map[string]map[string]uint{}
		for _, academy := range school.Member {
			p := map[string]uint{}
			for _, profession := range academy.Member {
				p[profession.Name] = profession.ID
			}
			a[academy.Name] = p
		}
		mp[school.Name] = a
	}
	// b, _ := json.Marshal(mp)
	return mp
}

// 根据consitituteID 获取 gradeId  professionId  academyId  map
func (c *Constitute) GetupConfigMap(consId uint) (out map[string]model.ConstituteUpdateRes, err error) {
	out = make(map[string]model.ConstituteUpdateRes, 0)
	var crs Constitute
	d := variable.DB.MySQL.Where("id = ?", consId).Find(&crs)
	if d.RowsAffected == 0 {
		return out, nil
	}
	var cs model.ConstituteUpdateRes
	if crs.Type == GradeType {
		cs.Id = crs.ID
		cs.Type = crs.Type
		cs.Name = crs.Name
		out[GradeType] = cs
		var belongToId = *(crs.BelongToID)
		crs = Constitute{}
		d = variable.DB.MySQL.Where("id = ?", belongToId).Find(&crs)
		if d.RowsAffected == 0 {
			return out, nil
		}
	}
	if crs.Type == ProfessionType {
		cs.Id = crs.ID
		cs.Type = crs.Type
		cs.Name = crs.Name
		out[ProfessionType] = cs
		var belongToId = *(crs.BelongToID)
		crs = Constitute{}
		d = variable.DB.MySQL.Where("id = ?", belongToId).Find(&crs)
		if d.RowsAffected == 0 {
			return out, nil
		}

	}
	if crs.Type == AcademyType {
		cs.Id = crs.ID
		cs.Type = crs.Type
		cs.Name = crs.Name
		out[AcademyType] = cs
		var belongToId = *(crs.BelongToID)
		crs = Constitute{}
		d = variable.DB.MySQL.Where("id = ?", belongToId).Find(&crs)
		if d.RowsAffected == 0 {
			return out, nil
		}
	}
	if crs.Type == SchoolType {
		cs.Id = crs.ID
		cs.Type = crs.Type
		cs.Name = crs.Name
		out[SchoolType] = cs
	}
	return out, nil
}

// 根据consitituteID 获取 gradeId  professionId  academyId
func (c *Constitute) GetupConfig(consId uint) (out model.GetupResp, err error) {
	var crs Constitute
	d := variable.DB.MySQL.Where("id = ?", consId).Find(&crs)
	if d.RowsAffected == 0 {
		return out, nil
	}
	if crs.Type == GradeType {
		out.GradeId = crs.ID
		var belongToId = *(crs.BelongToID)
		crs = Constitute{}
		d = variable.DB.MySQL.Where("id = ?", belongToId).Find(&crs)
		if d.RowsAffected == 0 {
			return out, nil
		}
	}
	if crs.Type == ProfessionType {
		out.ProfessionId = crs.ID
		var belongToId = *(crs.BelongToID)
		crs = Constitute{}
		d = variable.DB.MySQL.Where("id = ?", belongToId).Find(&crs)
		if d.RowsAffected == 0 {
			return out, nil
		}

	}
	if crs.Type == AcademyType {
		out.AcademyId = crs.ID
		var belongToId = *(crs.BelongToID)
		crs = Constitute{}
		d = variable.DB.MySQL.Where("id = ?", belongToId).Find(&crs)
		if d.RowsAffected == 0 {
			return out, nil
		}
	}
	if crs.Type == SchoolType {
		out.SchoolId = crs.ID
	}
	return out, nil
}

// 根据belong_id获取agentId
func (c *Constitute) GetIdsById(Id int64) (uint, uint, error) {
	// 获取agent_id
	var cs []Constitute
	var agentId uint = 0
	var schoolId uint = 0
	var whereStr = fmt.Sprintf("id = %d", Id)
	if d := variable.DB.MySQL.Model(&Constitute{}).Where(whereStr).FindInBatches(&cs, 1, func(tx *gorm.DB, batch int) error {
		var c Constitute
		if cs[0].Type == "school" {
			agentId = *cs[0].AgentID
			schoolId = cs[0].ID
		} else if cs[0].Type == "academy" {
			if d := variable.DB.MySQL.Model(&Constitute{}).Where("id = ?", cs[0].BelongToID).Find(&c); d.Error != nil {
				return d.Error
			}
			agentId = *c.AgentID
			schoolId = c.ID
		} else if cs[0].Type == "profession" {
			if d := variable.DB.MySQL.Model(&Constitute{}).Where("id = ?", cs[0].BelongToID).Preload("BelongTo").Find(&c); d.Error != nil {
				return d.Error
			}

			agentId = *c.BelongTo.AgentID
			schoolId = c.BelongTo.ID
		}
		return nil
	}); d.Error != nil {
		return 0, 0, d.Error
	}
	return agentId, schoolId, nil
}
